package com.jlf.core.engine.builder;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

import java.io.File;
import java.io.FileOutputStream;


/**
 * 持久化的自动构建
 *
 * @author wujr
 * 2023/7/7
 * @version 1.0
 * 【变更历史】
 * [wujr 2023/7/7 1.0 新增]
 */
@Slf4j
@Getter
public class AutoPersistenceBuilder {
    private final boolean savePersistenceClass = false;
    private final AutoDaoBuilder daoBuilder;
    private final AutoDomainBuilder domainBuilder;

    private Class<?> persistenceClass;
    private Class<?> persistenceImplClass;
    private String asmPersistenceInternal;
    private String asmPersistenceImplInternal;
    private String persistenceType;
    private String persistenceImplType;

    public AutoPersistenceBuilder(AutoDaoBuilder daoBuilder){
        this.daoBuilder = daoBuilder;
        this.domainBuilder = daoBuilder.getDomainBuilder();
    }
    /**
     * 构建模型领域的持久化服务接口
     * 本方法采用asm
     * @return 模型的持久化服务接口
     * @throws Exception 构建失败，抛出异常
     */
    public Class<?> generate() throws Exception{
        String persistenceClass = this.domainBuilder.getBasePackage() + ".persistence." + this.domainBuilder.getDomainSimpleCode() + "Persistence";
        try{
            this.persistenceClass = this.domainBuilder.getEngineClassLoader().loadClass(persistenceClass);
            this.asmPersistenceInternal = Type.getInternalName(this.persistenceClass);
            this.persistenceType = Type.getDescriptor(this.persistenceClass);
            return this.persistenceClass;
        }catch (Exception e){
            log.trace("加载模型领域：{} 的persistence，该persistence不存在，需要动态创建", this.domainBuilder.getDomainSimpleCode());
        }
        this.asmPersistenceInternal = AutoBuilderTool.class2Asm(persistenceClass);
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
        String signature = AutoBuilderTool.asmSimplePersistenceSignature("L" + this.domainBuilder.getAsmDomainInternal() + ";");
        cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT, asmPersistenceInternal, signature, "java/lang/Object", new String[]{AutoBuilderTool._asmSimplePersistenceInternal});
        cw.visitEnd();
        byte[] code = cw.toByteArray();
        if (savePersistenceClass) {
            String tempPath = System.getProperty("java.io.tmpdir") + File.separator;
            FileOutputStream fos = new FileOutputStream(tempPath + this.domainBuilder.getDomainSimpleCode() + "Persistence.class");
            fos.write(code);
            fos.close();
        }
        this.persistenceClass = this.domainBuilder.getEngineClassLoader().defineClass(persistenceClass, code);
        this.persistenceType = Type.getDescriptor(this.persistenceClass);
        return this.persistenceClass;
    }
    public Class<?> generateImpl() throws Exception{
        String persistencePackage = persistenceClass.getPackage().getName();
        String implPackage = persistencePackage + ".impl";
        String implClass = implPackage + "." + persistenceClass.getSimpleName() + "Impl";
        try{
            this.persistenceImplClass = this.domainBuilder.getEngineClassLoader().loadClass(implClass);
            this.asmPersistenceImplInternal = Type.getInternalName(this.persistenceImplClass);
            this.persistenceImplType = Type.getDescriptor(this.persistenceImplClass);
            return this.persistenceImplClass;
        }catch (Exception e){
            log.trace("加载模型领域：{} 的persistenceImpl，该persistenceImpl不存在，需要动态创建", this.domainBuilder.getDomainClass().getSimpleName());
        }
        this.asmPersistenceImplInternal = AutoBuilderTool.class2Asm(implClass);
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
        String signature = AutoBuilderTool.asmSimplePersistenceImplSignature("L" + this.daoBuilder.getAsmDaoInternal() + ";", "L" + this.domainBuilder.getAsmDomainInternal() + ";", "L" + asmPersistenceInternal + ";");
        String[] interfaces = {asmPersistenceInternal};
        cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, asmPersistenceImplInternal, signature, AutoBuilderTool._asmSimplePersistenceImplInternal, interfaces);
        // 添加默认构造函数
        AutoBuilderTool.asmDefaultConstructor(cw, AutoBuilderTool._asmSimplePersistenceImplInternal, true);
        cw.visitEnd();
        byte[] code = cw.toByteArray();
        if (savePersistenceClass) {
            String tempPath = System.getProperty("java.io.tmpdir") + File.separator;
            FileOutputStream fos = new FileOutputStream(tempPath + persistenceClass.getSimpleName() + "Impl.class");
            fos.write(code);
            fos.close();
        }
        this.persistenceImplClass = this.domainBuilder.getEngineClassLoader().defineClass(implClass, code);
        this.persistenceImplType = Type.getDescriptor(this.persistenceImplClass);
        return this.persistenceImplClass;
    }
}
